package eu.irreality.dai.fov;

import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.LinkedList;
import java.util.List;

import eu.irreality.dai.util.Position;
import eu.irreality.dai.world.level.Level;

public class SimpleLOS implements LOSCriterion
{

    private VisibilityBlockingCriterion vc;

    public SimpleLOS(VisibilityBlockingCriterion vc)
    {
	this.vc = vc;
	Rectangle r = new Rectangle();
    }

    /*
     * private Point pointFor ( Position p ) {
     * 
     * }
     */

    public boolean isInLOS(Level l, Position source, Position target)
    {
	if (source.equals(target))
	    return true;
	for (int corner = NW; corner <= SE; corner++)
	{
	    boolean ok = true;
	    Point2D sourcePoint = cornerFor(source, corner);
	    Point2D targetPoint = cornerFor(target, corner);
	    List<Position> blockingPositions = getPositionsInLine(sourcePoint,
		    targetPoint);
	    for (Position p : blockingPositions)
	    {
		if (vc.blocksVision(l, p) && !p.equals(source)
			&& !p.equals(target))
		{
		    ok = false;
		    break;
		}
	    }
	    if (ok)
		return true;
	}
	return false;
    }

    // corners
    private int NW = 0;
    private int NE = 1;
    private int SW = 2;
    private int SE = 3;

    private List<Position> getPositionsInLine(Point2D p1, Point2D p2)
    {
	Line2D l = new Line2D.Double(p1, p2);
	Rectangle bounds = l.getBounds();
	int minRow = (int) Math.floor(bounds.getMinY());
	int minCol = (int) Math.floor(bounds.getMinX());
	int maxRow = (int) Math.ceil(bounds.getMaxY());
	int maxCol = (int) Math.ceil(bounds.getMaxX());
	List<Position> result = new LinkedList();
	for (int r = minRow; r <= maxRow; r++)
	{
	    for (int c = minCol; c <= maxCol; c++)
	    {
		Position pos = new Position(r, c);
		Rectangle rect = rectangleFor(pos);
		if (l.intersects(rect))
		    result.add(pos);
	    }
	}
	return result;
    }

    private Point2D cornerFor(Position p, int corner)
    {
	if (corner == NW)
	    return new Point2D.Double(p.getCol() + 0.1, p.getRow() + 0.1);
	else if (corner == NE)
	    return new Point2D.Double(p.getCol() + 1 - 0.1, p.getRow() + 0.1);
	else if (corner == SW)
	    return new Point2D.Double(p.getCol() + 0.1, p.getRow() + 1 - 0.1);
	else
	    return new Point2D.Double(p.getCol() + 1 - 0.1,
		    p.getRow() + 1 - 0.1);
    }

    private Position positionFor(Point p)
    {
	double x = p.getX();
	double y = p.getY();
	return new Position((int) Math.floor(y), (int) Math.floor(x));
    }

    private Rectangle rectangleFor(Position p)
    {
	return new Rectangle(p.getCol(), p.getRow(), 1, 1);
    }

}
